﻿using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using Gma.UserActivityMonitor;
using wmkMacro.Properties;

namespace wmkMacro.Forms
{
    public partial class MainForm : Form
    {
        #region Interop Methods

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

        #endregion

        #region Constants Mouse Fields Values

        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;
        private const int MOUSEEVENTF_MIDDLEDOWN = 0x20;
        private const int MOUSEEVENTF_MIDDLEUP = 0x40;

        #endregion

        #region Private Fields

        private RecordActions recordActions = new RecordActions();
        private Boolean isPlaying = false;
        private Boolean hold = false;

        #endregion

        #region Constructor

        public MainForm()
        {
            InitializeComponent();
            this.RegisterHooks();
        }

        #endregion

        #region Form Methods

        private void MainForm_Shown(object sender, EventArgs e)
        {
            this.Hide();
            this.Opacity = 100;
            this.LoadOptionScreen();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                this.Hide();
                e.Cancel = true;
            }
            else
                this.ReleaseHooks();
        }

        #endregion

        #region Input Hook Methods

        private void ReleaseHooks()
        {
            HookManager.MouseMove -= new MouseEventHandler(HookManager_MouseMove);
            HookManager.MouseDown -= new MouseEventHandler(HookManager_MouseDown);
            HookManager.MouseUp -= new MouseEventHandler(HookManager_MouseUp);

            HookManager.KeyUp -= new KeyEventHandler(HookManager_KeyUp);
            //HookManager.KeyDown -= new KeyEventHandler(HookManager_KeyDown);
        }

        private void RegisterHooks()
        {
            HookManager.MouseMove += new MouseEventHandler(HookManager_MouseMove);
            HookManager.MouseDown += new MouseEventHandler(HookManager_MouseDown);
            HookManager.MouseUp += new MouseEventHandler(HookManager_MouseUp);

            HookManager.KeyUp += new KeyEventHandler(HookManager_KeyUp);
            //HookManager.KeyDown += new KeyEventHandler(HookManager_KeyDown);
        }

        private void HookManager_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.recordActions.IsRecording)
            {
                this.recordActions.Add(new RecordStep(RecordAction.Move, e));
            }
        }

        private void HookManager_MouseDown(object sender, MouseEventArgs e)
        {
            if (this.recordActions.IsRecording)
            {
                this.recordActions.Add(new RecordStep(RecordAction.ButtonDown, e));
            }
        }

        private void HookManager_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.recordActions.IsRecording)
            {
                this.recordActions.Add(new RecordStep(RecordAction.ButtonUp, e));
            }
        }

        private void HookManager_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Control || e.KeyData == Keys.LControlKey)
            {
                this.RecordToolStripMenuItem_Click(this, EventArgs.Empty);
            }
            else if (e.Alt && this.isPlaying)
            {
                //TODO: Stop Playing
            }
            else if (this.recordActions.IsRecording)
            {
                this.recordActions.Add(new RecordStep(RecordAction.KeyUp, e));
            }
            //this.isPlaying && 
            else if (e.KeyCode == Keys.Escape)
            {
                this.hold = true;
            }
        }

      /*  private void HookManager_KeyDown(object sender, KeyEventArgs e)
        {
            if (this.isRecording)
            {
                this.recordActions.Add(new RecordStep(RecordAction.KeyDown, e));
            }
        }*/

        #endregion

        #region Mouse Methods

        private Point GetMousePoint()
        {
            return new Point(Cursor.Position.X, Cursor.Position.Y);
        }

        private String AdjustKeyData(Keys keyData)
        {
            return keyData.ToString().Replace("Return", "ENTER")
                                     .Replace("Arrow", string.Empty)
                                     .Replace("PageDown", "PGDN")
                                     .Replace("PageUp", "PGUP")
                                     .Replace("Escape", "ESC")
                                     .Replace("LControlKey", "^")
                                     .Replace("RControlKey", "^")
                                     .Replace("LShiftKey", "+")
                                     .Replace("RShiftKey", "+")
                                     .Replace("Back", "BACKSPACE")
                                     .Replace("PrintScreen", "PRTSC");
        }

        private void DoKeyboardEvent(KeyEventArgs eventArgs, RecordAction action)
        {
            SendKeys.SendWait("{"+AdjustKeyData(eventArgs.KeyData).ToLower()+"}");
        }

        private void DoMouseEvent(MouseEventArgs eventArgs, RecordAction action)
        {
            switch (eventArgs.Button)
            {
                case MouseButtons.Left:
                    this.DoLeftMouseEvent(action);
                    break;
                case MouseButtons.Right:
                    this.DoRightMouseEvent(action);
                    break;
                case MouseButtons.Middle:
                    this.DoMiddleMouseEvent(action);
                    break;
            }
        }

        private void DoLeftMouseEvent(RecordAction action)
        {
            Point mousePoint = this.GetMousePoint();
            if (action == RecordAction.ButtonDown)
                mouse_event(MOUSEEVENTF_LEFTDOWN, mousePoint.X, mousePoint.Y, 0, 0);
            else
                mouse_event(MOUSEEVENTF_LEFTUP, mousePoint.X, mousePoint.Y, 0, 0);
        }

        private void DoRightMouseEvent(RecordAction action)
        {
            Point mousePoint = this.GetMousePoint();
            if (action == RecordAction.ButtonDown)
                mouse_event(MOUSEEVENTF_RIGHTDOWN, mousePoint.X, mousePoint.Y, 0, 0);
            else
                mouse_event(MOUSEEVENTF_RIGHTUP, mousePoint.X, mousePoint.Y, 0, 0);
        }

        private void DoMiddleMouseEvent(RecordAction action)
        {
            Point mousePoint = this.GetMousePoint();
            if (action == RecordAction.ButtonDown)
                mouse_event(MOUSEEVENTF_MIDDLEDOWN, mousePoint.X, mousePoint.Y, 0, 0);
            else
                mouse_event(MOUSEEVENTF_MIDDLEUP, mousePoint.X, mousePoint.Y, 0, 0);
        }

        #endregion

        #region Menu Methods

        private void UpdateMenuItems()
        {
            this.RecordToolStripMenuItem.Text = (this.recordActions.IsRecording) ? "Stop &Record" : "&Record";
            this.PlayToolStripMenuItem.Enabled = (!this.recordActions.IsRecording && this.recordActions.Items.Any());

            this.MainNotifyIcon.ShowBalloonTip(2000, "Action", (!this.recordActions.IsRecording) ? "Stop Recording" : "Recording", ToolTipIcon.Warning);
        }

        private void RecordToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.recordActions.IsRecording = !this.recordActions.IsRecording;

            if (this.recordActions.IsRecording)
                this.recordActions.Items.Clear();

            this.UpdateMenuItems();
        }

        private void PlayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Move to a thread to be able to stop play
            this.isPlaying = true;
            this.MainNotifyIcon.ShowBalloonTip(2000, "Action", "Playing", ToolTipIcon.Warning);

            new Thread(ThreadPlay).Start();
            //ThreadPlay();

            this.isPlaying = false;
        }

        private void ThreadPlay()
        {
            for (int i = 0; i < Settings.Default.PlayLoopCountSetting; i++)
            {
                if (!this.hold)
                {
                    this.recordActions.Items.ToList().ForEach
                    (
                        delegate(RecordStep recordStep)
                        {
                            if (!this.hold)
                            {
                                Thread.Sleep((Int32)recordStep.Delay);

                                switch (recordStep.Action)
                                {
                                    case RecordAction.ButtonDown:
                                    case RecordAction.ButtonUp:
                                        DoMouseEvent(((MouseEventArgs)recordStep.Event), recordStep.Action);
                                        break;
                                    case RecordAction.KeyDown:
                                    case RecordAction.KeyUp:
                                        DoKeyboardEvent(((KeyEventArgs)recordStep.Event), recordStep.Action);
                                        break;
                                    case RecordAction.Move:
                                        Cursor.Position = new Point(
                                            ((MouseEventArgs)recordStep.Event).X,
                                            ((MouseEventArgs)recordStep.Event).Y);
                                        break;
                                    default:
                                        return;
                                }
                            }
                        }
                    );
                }
                else
                {
                    this.MainNotifyIcon.ShowBalloonTip(2000, "Play", "Play canceled", ToolTipIcon.Warning);
                    return;
                }
            }
        }

        private void OptionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Show();
        }

        private void CloseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new MainAboutBox().ShowDialog(this);
        }

        #endregion

        #region Option Methods

        private void LoadOptionScreen()
        {
            this.PlayLoopCountMaskedTextBox.Text = Settings.Default.PlayLoopCountSetting.ToString();
        }

        private void SaveButton_Click(object sender, EventArgs e)
        {
            Settings.Default.PlayLoopCountSetting = Int32.Parse(this.PlayLoopCountMaskedTextBox.Text);
            Settings.Default.Save();
        }

        #endregion
    }
}